package com.jinunn.system.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.text.StrPool;
import com.jinunn.system.common.constant.SystemConstants;
import com.jinunn.system.converter.DeptConverter;
import com.jinunn.system.entity.Dept;
import com.jinunn.system.exception.BusinessException;
import com.jinunn.system.mapper.DeptMapper;
import com.jinunn.system.model.params.DeptParam;
import com.jinunn.system.model.query.DeptQuery;
import com.jinunn.system.model.vo.dept.DeptVO;
import com.jinunn.system.service.DeptService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * <p>
 * 部门表 服务实现类
 * </p>
 *
 * @author JIN
 * @since 2024-07-12
 */
@Service
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class DeptServiceImpl extends ServiceImpl<DeptMapper, Dept> implements DeptService {

    private final DeptConverter deptConverter;

    @Override
    public List<DeptVO> getDeptList(DeptQuery queryParams) {
        String keywords = queryParams.getKeywords();
        Integer status = queryParams.getStatus();
        List<Dept> deptList = this.lambdaQuery()
                .like(StringUtils.isNotBlank(keywords), Dept::getName, keywords)
                .eq(Objects.nonNull(status), Dept::getStatus, status)
                .orderByDesc(Dept::getSort)
                .list();
        if (CollectionUtil.isEmpty(deptList)) {
            return Collections.emptyList();
        }

        // 获取所有部门ID
        Set<Long> deptIds = deptList.stream()
                .map(Dept::getId).collect(Collectors.toSet());
        // 获取父节点ID
        Set<Long> parentIds = deptList.stream()
                .map(Dept::getParentId).collect(Collectors.toSet());
        // 获取根节点ID（递归的起点）,意思是父节点ID中不包含在部门ID中的节点 差集
        // 注意这里不能拿顶级部门 O 作为根节点，因为部门筛选的时候 O 会被过滤掉
        List<Long> rootIds = CollectionUtil.subtractToList(parentIds, deptIds);
        // 递归生成部门树形列表
        return rootIds.stream()
                .flatMap(rootId -> this.getChildrenList(rootId, deptList).stream())
                .toList();
    }

    @Override
    public Long saveDept(DeptParam formData) {
        // 校验部门名称是否存在
        String code = formData.getCode();
        Long count = this.lambdaQuery()
                .eq(Dept::getCode, code).count();
        Assert.isTrue(count == 0, () -> new BusinessException("部门编号已存在"));

        Dept entity = deptConverter.convertToEntity(formData);

        // 生成部门路径(tree_path)，格式：父节点tree_path + , + 父节点ID，用于删除部门时级联删除子部门
        String treePath = this.generateDeptTreePath(formData.getParentId());
        entity.setTreePath(treePath);

        // 保存部门并返回部门ID
        boolean result = this.save(entity);
        Assert.isTrue(result, () -> new BusinessException("部门保存失败"));
        return entity.getId();
    }


    /**
     * 递归生成部门树形列表
     *
     * @param parentId 父ID
     * @param deptList 部门列表
     * @return 部门树形列表
     */
    private List<DeptVO> getChildrenList(Long parentId, List<Dept> deptList) {
        return deptList.stream()
                .filter(dept -> dept.getParentId().equals(parentId))
                .map(dept -> {
                    DeptVO deptVO = deptConverter.convertToVo(dept);
                    deptVO.setChildren(this.getChildrenList(dept.getId(), deptList));
                    return deptVO;
                }).toList();
    }

    /**
     * 部门路径生成
     *
     * @param parentId 父ID
     * @return 父节点路径以英文逗号(, )分割，eg: 1,2,3
     */
    private String generateDeptTreePath(Long parentId) {
        String treePath = null;
        if (SystemConstants.ROOT_NODE_ID.equals(parentId)) {
            treePath = String.valueOf(parentId);
        } else {
            Dept parent = this.getById(parentId);
            if (parent != null) {
                treePath = parent.getTreePath() + StrPool.C_COMMA + parent.getId();
            }
        }
        return treePath;
    }
}
